﻿//BaseTrader.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Threading;

	using Ecng.Collections;
	using Ecng.Common;
	using Ecng.ComponentModel;
	using Ecng.Interop;
	using Ecng.Serialization;

	using StockSharp.Algo.Licensing;
	using StockSharp.Logging;
	using StockSharp.BusinessEntities;

	/// <summary>
	/// Базовый класс для создания шлюзов к торговым системам.
	/// </summary>
	public abstract class BaseTrader : BaseLogReceiver<ITrader>, ITrader
	{
		private sealed class Cache
		{
			private sealed class SecurityData
			{
				public readonly CachedSynchronizedDictionary<long, MyTrade> MyTrades = new CachedSynchronizedDictionary<long, MyTrade>();
				public readonly CachedSynchronizedDictionary<long, Order> Orders = new CachedSynchronizedDictionary<long, Order>();
				public readonly SynchronizedDictionary<long, Trade> Trades = new SynchronizedDictionary<long, Trade>();
			}

			private readonly SynchronizedDictionary<Security, SecurityData> _securityData = new SynchronizedDictionary<Security, SecurityData>();

			private DateTime _firstTradeTime;
			private DateTime _lastTradeTime;
			private TimeSpan _maxTradesKeepTime;

			public IEnumerable<Trade> Trades
			{
				get { return _securityData.SyncGet(d => d.SelectMany(p => p.Value.Trades.SyncGet(trades => trades.Values.ToArray())).ToArray()); }
			}

			private TimeSpan _tradesKeepTime;

			public TimeSpan TradesKeepTime
			{
				get { return _tradesKeepTime; }
				set
				{
					if (value < TimeSpan.Zero)
						throw new ArgumentOutOfRangeException("value", value, "Время хранения тиковых сделок не может быть отрицательным.");

					_tradesKeepTime = value;
					_maxTradesKeepTime = TimeSpan.FromTicks((long)(value.Ticks * 1.5));
					RecycleTrades();
				}
			}

			private readonly CachedSynchronizedList<MyTrade> _myTrades = new CachedSynchronizedList<MyTrade>();

			public IEnumerable<MyTrade> MyTrades
			{
				get { return _myTrades.Cache; }
			}

			private readonly CachedSynchronizedList<Order> _orders = new CachedSynchronizedList<Order>();

			public IEnumerable<Order> Orders
			{
				get { return _orders.Cache; }
			}

			public Trade GetTrade(Security security, long id, Func<long, Trade> createTrade)
			{
				return GetData(security).Trades.SafeAdd(id, key =>
				{
					var trade = createTrade(key);

					if (_firstTradeTime == default(DateTime))
						_firstTradeTime = trade.Time;

					_lastTradeTime = trade.Time;

					MemoryStatistics.Instance.Trades.Add(trade);

					RecycleTrades();

					return trade;
				});
			}

			private void RecycleTrades()
			{
				if (TradesKeepTime == TimeSpan.Zero)
					return;

				var diff = _lastTradeTime - _firstTradeTime;

				if (diff <= _maxTradesKeepTime)
					return;

				lock (_securityData.SyncRoot)
				{
					_firstTradeTime = _lastTradeTime - TradesKeepTime;

					foreach (var pair in _securityData)
					{
						var removedTrades = pair.Value.Trades.SyncGet(d => d.RemoveWhere(p => p.Value.Time < _firstTradeTime));
						MemoryStatistics.Instance.Trades.Remove(removedTrades.Select(t => t.Value));
					}
				}
			}

			public Order GetOrder(Security security, long id, Func<long, Order> createOrder)
			{
				return GetData(security).Orders.SafeAdd(id, key =>
				{
					var order = createOrder(key);
					_orders.Add(order);
					return order;
				});
			}

			public Order TryGetOrder(Security security, long id)
			{
				return GetData(security).Orders.TryGetValue(id);
			}

			public MyTrade TryGetMyTrade(Security security, long id)
			{
				return GetData(security).MyTrades.TryGetValue(id);
			}

			public MyTrade GetMyTrade(Security security, long id, Func<long, MyTrade> createTrade)
			{
				return GetData(security).MyTrades.SafeAdd(id, key =>
				{
					var myTrade = createTrade(key);
					_myTrades.Add(myTrade);
					return myTrade;
				});
			}

			private SecurityData GetData(Security security)
			{
				return _securityData.SafeAdd(security);
			}
		}

		private sealed class SubscriptionManager
		{
			private readonly BaseTrader _trader;

			private enum ContinuousTypes
			{
				Security,
				Trades,
				MarketDepth,
				OrderLog,
			}

			private sealed class ContinuousInfo : Tuple<ContinuousSecurity, ContinuousTypes>
			{
				public ContinuousInfo(ContinuousSecurity security, ContinuousTypes type)
					: base(security, type)
				{
				}

				public TimeSpan Elapsed { get; set; }
			}

			private readonly SynchronizedLinkedList<ContinuousInfo> _continuousSecurities = new SynchronizedLinkedList<ContinuousInfo>();

			public SubscriptionManager(BaseTrader trader)
			{
				if (trader == null)
					throw new ArgumentNullException("trader");

				_trader = trader;
			}

			private readonly CachedSynchronizedDictionary<Security, int> _registeredSecurities = new CachedSynchronizedDictionary<Security, int>();

			public IEnumerable<Security> RegisteredSecurities
			{
				get { return _registeredSecurities.CachedKeys; }
			}

			private readonly CachedSynchronizedDictionary<Security, int> _registeredMarketDepths = new CachedSynchronizedDictionary<Security, int>();

			public IEnumerable<Security> RegisteredMarketDepths
			{
				get { return _registeredMarketDepths.CachedKeys; }
			}

			private readonly CachedSynchronizedDictionary<Security, int> _registeredTrades = new CachedSynchronizedDictionary<Security, int>();

			public IEnumerable<Security> RegisteredTrades
			{
				get { return _registeredTrades.CachedKeys; }
			}

			private readonly CachedSynchronizedDictionary<Security, int> _registeredOrderLogs = new CachedSynchronizedDictionary<Security, int>();

			public IEnumerable<Security> RegisteredOrderLogs
			{
				get { return _registeredOrderLogs.CachedKeys; }
			}

			private readonly CachedSynchronizedDictionary<Portfolio, int> _registeredPortfolios = new CachedSynchronizedDictionary<Portfolio, int>();

			public IEnumerable<Portfolio> RegisteredPortfolios
			{
				get { return _registeredPortfolios.CachedKeys; }
			}

			public void RegisterSecurity(Security security)
			{
				if (security == null)
					throw new ArgumentNullException("security");

				if (security is IndexSecurity)
					((IndexSecurity)security).InnerSecurities.ForEach(_trader.RegisterSecurity);
				else if (security is ContinuousSecurity)
					SubscribeContinuous((ContinuousSecurity)security, ContinuousTypes.Security);
				else if (TrySubscribe(_registeredSecurities, security))
					_trader.OnRegisterSecurity(security);
			}

			public void UnRegisterSecurity(Security security)
			{
				if (security == null)
					throw new ArgumentNullException("security");

				if (security is IndexSecurity)
					((IndexSecurity)security).InnerSecurities.ForEach(_trader.UnRegisterSecurity);
				else if (security is ContinuousSecurity)
					UnSubscribeContinuous((ContinuousSecurity)security, ContinuousTypes.Security);
				else if (TryUnSubscribe(_registeredSecurities, security))
					_trader.OnUnRegisterSecurity(security);
			}

			public void RegisterTrades(Security security)
			{
				if (security == null)
					throw new ArgumentNullException("security");

				if (security is IndexSecurity)
					((IndexSecurity)security).InnerSecurities.ForEach(_trader.RegisterTrades);
				else if (security is ContinuousSecurity)
					SubscribeContinuous((ContinuousSecurity)security, ContinuousTypes.Trades);
				else if (TrySubscribe(_registeredTrades, security))
					_trader.OnRegisterTrades(security);
			}

			public void UnRegisterTrades(Security security)
			{
				if (security == null)
					throw new ArgumentNullException("security");

				if (security is IndexSecurity)
					((IndexSecurity)security).InnerSecurities.ForEach(_trader.UnRegisterTrades);
				else if (security is ContinuousSecurity)
					UnSubscribeContinuous((ContinuousSecurity)security, ContinuousTypes.Trades);
				else if (TryUnSubscribe(_registeredTrades, security))
					_trader.OnUnRegisterTrades(security);
			}

			public void RegisterMarketDepth(Security security)
			{
				if (security == null)
					throw new ArgumentNullException("security");

				if (security is IndexSecurity)
					((IndexSecurity)security).InnerSecurities.ForEach(_trader.RegisterMarketDepth);
				else if (security is ContinuousSecurity)
					SubscribeContinuous((ContinuousSecurity)security, ContinuousTypes.MarketDepth);
				else if (TrySubscribe(_registeredMarketDepths, security))
					_trader.OnRegisterMarketDepth(security);
			}

			public void UnRegisterMarketDepth(Security security)
			{
				if (security == null)
					throw new ArgumentNullException("security");

				if (security is IndexSecurity)
					((IndexSecurity)security).InnerSecurities.ForEach(_trader.UnRegisterMarketDepth);
				else if (security is ContinuousSecurity)
					UnSubscribeContinuous((ContinuousSecurity)security, ContinuousTypes.MarketDepth);
				else if (TryUnSubscribe(_registeredMarketDepths, security))
					_trader.OnUnRegisterMarketDepth(security);
			}

			public void RegisterOrderLog(Security security)
			{
				if (security == null)
					throw new ArgumentNullException("security");

				if (security is IndexSecurity)
					((IndexSecurity)security).InnerSecurities.ForEach(_trader.RegisterOrderLog);
				else if (security is ContinuousSecurity)
					SubscribeContinuous((ContinuousSecurity)security, ContinuousTypes.OrderLog);
				else if (TrySubscribe(_registeredOrderLogs, security))
					_trader.OnRegisterOrderLog(security);
			}

			public void UnRegisterOrderLog(Security security)
			{
				if (security == null)
					throw new ArgumentNullException("security");

				if (security is IndexSecurity)
					((IndexSecurity)security).InnerSecurities.ForEach(_trader.UnRegisterOrderLog);
				else if (security is ContinuousSecurity)
					UnSubscribeContinuous((ContinuousSecurity)security, ContinuousTypes.OrderLog);
				else if (TryUnSubscribe(_registeredOrderLogs, security))
					_trader.OnUnRegisterOrderLog(security);
			}

			public void RegisterPortfolio(Portfolio portfolio)
			{
				if (portfolio == null)
					throw new ArgumentNullException("portfolio");

				if (portfolio is BasketPortfolio)
					((BasketPortfolio)portfolio).InnerPortfolios.ForEach(_trader.RegisterPortfolio);
				else if (TrySubscribe(_registeredPortfolios, portfolio))
					_trader.OnRegisterPortfolio(portfolio);
			}

			public void UnRegisterPortfolio(Portfolio portfolio)
			{
				if (portfolio == null)
					throw new ArgumentNullException("portfolio");

				if (portfolio is BasketPortfolio)
					((BasketPortfolio)portfolio).InnerPortfolios.ForEach(_trader.UnRegisterPortfolio);
				else if (TryUnSubscribe(_registeredPortfolios, portfolio))
					_trader.OnUnRegisterPortfolio(portfolio);
			}

			private void SubscribeContinuous(ContinuousSecurity security, ContinuousTypes type)
			{
				lock (_continuousSecurities.SyncRoot)
				{
					var info = new ContinuousInfo(security, type);

					if (!_continuousSecurities.Contains(info))
					{
						_continuousSecurities.AddFirst(info);

						if (_continuousSecurities.Count == 1)
							_trader.MarketTimeChanged += TraderOnMarketTimeChanged;
					}
				}
			}

			private void UnSubscribeContinuous(ContinuousSecurity security, ContinuousTypes type)
			{
				lock (_continuousSecurities.SyncRoot)
				{
					var node = _continuousSecurities.Find(new ContinuousInfo(security, type));

					if (node != null)
					{
						var diff = node.Value.Elapsed;
						var curr = node;

						while (curr != null)
						{
							curr.Value.Elapsed += diff;
							curr = curr.Next;
						}

						_continuousSecurities.Remove(node);

						if (_continuousSecurities.Count == 0)
							_trader.MarketTimeChanged -= TraderOnMarketTimeChanged;
					}
				}
			}

			private static DateTime NextExpInUtc(LinkedListNode<ContinuousInfo> node)
			{
				if (node == null)
					throw new ArgumentNullException("node");

				var contSec = node.Value.Item1;
				var currSec = contSec.GetSecurity();
				return currSec.ExchangeBoard.Exchange.ToUtc(contSec.ExpirationJumps[currSec]);
			}

			private void TraderOnMarketTimeChanged(TimeSpan diff)
			{
				var first = _continuousSecurities.First;

				if (first == null)
					return;

				if (first.Value.Elapsed > diff)
					first.Value.Elapsed -= diff;
				else
				{
					var underlyingSecurities = new List<Tuple<ContinuousSecurity, ContinuousTypes, Security, Security>>();

					lock (_continuousSecurities.SyncRoot)
					{
						var curr = first;

						while (curr != null && curr.Value.Elapsed <= diff && diff > TimeSpan.Zero)
						{
							diff -= curr.Value.Elapsed;
							_continuousSecurities.Remove(curr);

							var currSec = curr.Value.Item1.GetSecurity();

							if (currSec != null)
							{
								var jumpInUtc = NextExpInUtc(curr);

								var c = _continuousSecurities.First;

								while (c != null)
								{
									if (jumpInUtc < NextExpInUtc(c))
										break;

									c = c.Next;
								}

								if (c == null)
									_continuousSecurities.AddLast(curr);
								else
								{
									c.Value.Elapsed = NextExpInUtc(c) - jumpInUtc;
									_continuousSecurities.AddBefore(c, curr);
								}

								if (curr.Previous != null)
								{
									curr.Value.Elapsed = jumpInUtc - NextExpInUtc(curr.Previous);
								}
								else
								{
									curr.Value.Elapsed = jumpInUtc - currSec.ExchangeBoard.Exchange.ToUtc(currSec.GetMarketTime());
								}

								underlyingSecurities.Add(new Tuple<ContinuousSecurity, ContinuousTypes, Security, Security>(curr.Value.Item1, curr.Value.Item2, curr.Value.Item1.ExpirationJumps.GetPrevSecurity(currSec), currSec));
							}
							else
							{
								underlyingSecurities.Add(new Tuple<ContinuousSecurity, ContinuousTypes, Security, Security>(curr.Value.Item1, curr.Value.Item2, curr.Value.Item1.ExpirationJumps.LastSecurity, null));
								UnSubscribeContinuous(curr.Value.Item1, curr.Value.Item2);
							}

							curr = _continuousSecurities.First;
						}
					}

					foreach (var tuple in underlyingSecurities)
					{
						if (tuple.Item3 != null)
						{
							switch (tuple.Item2)
							{
								case ContinuousTypes.Security:
									UnRegisterSecurity(tuple.Item3);
									break;
								case ContinuousTypes.Trades:
									UnRegisterTrades(tuple.Item3);
									break;
								case ContinuousTypes.MarketDepth:
									UnRegisterMarketDepth(tuple.Item3);
									break;
								case ContinuousTypes.OrderLog:
									UnRegisterOrderLog(tuple.Item3);
									break;
								default:
									throw new ArgumentOutOfRangeException();
							}
						}

						if (tuple.Item4 != null)
						{
							switch (tuple.Item2)
							{
								case ContinuousTypes.Security:
									RegisterSecurity(tuple.Item4);
									break;
								case ContinuousTypes.Trades:
									RegisterTrades(tuple.Item4);
									break;
								case ContinuousTypes.MarketDepth:
									RegisterMarketDepth(tuple.Item4);
									break;
								case ContinuousTypes.OrderLog:
									RegisterOrderLog(tuple.Item4);
									break;
								default:
									throw new ArgumentOutOfRangeException();
							}
						}
					}
				}
			}

			private static bool TrySubscribe<T>(CachedSynchronizedDictionary<T, int> subscribers, T subscriber)
			{
				return ChangeSubscribers(subscribers, subscriber, 1) == 1;
			}

			private static bool TryUnSubscribe<T>(CachedSynchronizedDictionary<T, int> subscribers, T subscriber)
			{
				return ChangeSubscribers(subscribers, subscriber, -1) == 0;
			}

			private static int ChangeSubscribers<T>(CachedSynchronizedDictionary<T, int> subscribers, T subscriber, int delta)
			{
				if (subscribers == null)
					throw new ArgumentNullException("subscribers");

				lock (subscribers.SyncRoot)
				{
					var value = subscribers.TryGetValue2(subscriber) ?? 0;

					value += delta;

					if (value > 0)
						subscribers[subscriber] = value;
					else
						subscribers.Remove(subscriber);

					return value;
				}
			}

			public void ReStart()
			{
				try
				{
					RegisteredSecurities.ForEach(_trader.OnUnRegisterSecurity);
					RegisteredMarketDepths.ForEach(_trader.OnUnRegisterMarketDepth);
					RegisteredOrderLogs.ForEach(_trader.OnUnRegisterOrderLog);
					RegisteredTrades.ForEach(_trader.OnUnRegisterTrades);
					RegisteredPortfolios.ForEach(_trader.OnUnRegisterPortfolio);
				}
				catch (Exception ex)
				{
					_trader.RaiseProcessDataError(ex);
				}

				RegisteredSecurities.ForEach(_trader.OnRegisterSecurity);
				RegisteredMarketDepths.ForEach(_trader.OnRegisterMarketDepth);
				RegisteredOrderLogs.ForEach(_trader.OnRegisterOrderLog);
				RegisteredTrades.ForEach(_trader.OnRegisterTrades);
				RegisteredPortfolios.ForEach(_trader.OnRegisterPortfolio);
			}

			public void Stop()
			{
				RegisteredSecurities.ForEach(_trader.UnRegisterSecurity);
				RegisteredMarketDepths.ForEach(_trader.UnRegisterMarketDepth);
				RegisteredOrderLogs.ForEach(_trader.UnRegisterOrderLog);
				RegisteredTrades.ForEach(_trader.UnRegisterTrades);
				RegisteredPortfolios.ForEach(_trader.UnRegisterPortfolio);
			}
		}

		private readonly Cache _cache = new Cache();

		private readonly SynchronizedDictionary<Order, SynchronizedList<MyTrade>> _myTradesByOrder = new SynchronizedDictionary<Order, SynchronizedList<MyTrade>>();
		private readonly SortedDictionary<Security, MarketDepth> _marketDepths = new SortedDictionary<Security, MarketDepth>();
		private readonly SynchronizedDictionary<long, List<Trade>> _notOrderedByIdMyTrades = new SynchronizedDictionary<long, List<Trade>>();
		private readonly SynchronizedDictionary<long, List<Trade>> _notOrderedByTransactionMyTrades = new SynchronizedDictionary<long, List<Trade>>();
		private readonly SynchronizedDictionary<long, Order> _ordersByTransactionId = new SynchronizedDictionary<long, Order>();
		private readonly SynchronizedDictionary<long, Order> _stopOrdersByTransactionId = new SynchronizedDictionary<long, Order>();
		private readonly SynchronizedMultiDictionary<long, RefPair<Order, Action<Order, Order>>> _orderStopOrderAssociations = new SynchronizedMultiDictionary<long, RefPair<Order, Action<Order, Order>>>();

		private readonly Dictionary<object, Security> _nativeIdSecurities = new Dictionary<object, Security>();
		private readonly Dictionary<object, List<Action<Security>>> _suspendedNativeSecurityEvents = new Dictionary<object, List<Action<Security>>>();
		private readonly Dictionary<object, List<Action<Security>>> _suspendedSecurityEvents = new Dictionary<object, List<Action<Security>>>();
		private readonly object _suspendSync = new object();

		private readonly ReConnectionManager _reConnectionManager;

		private readonly EventsContainer<Security> _newSecuritiesEvent;
		private readonly EventsContainer<Security> _securitiesChangedEvent;
		private readonly EventsContainer<Order> _newOrdersEvent;
		private readonly EventsContainer<Order> _ordersChangedEvent;
		private readonly EventsContainer<OrderFail> _ordersRegisterFailedEvent;
		private readonly EventsContainer<OrderFail> _ordersCancelFailedEvent;
		private readonly EventsContainer<Order> _newStopOrdersEvent;
		private readonly EventsContainer<Order> _stopOrdersChangedEvent;
		private readonly EventsContainer<OrderFail> _stopOrdersRegisterFailedEvent;
		private readonly EventsContainer<OrderFail> _stopOrdersCancelFailedEvent;
		private readonly EventsContainer<MyTrade> _newMyTradesEvent;
		private readonly EventsContainer<Trade> _newTradesEvent;
		private readonly EventsContainer<MarketDepth> _marketDepthsChangedEvent;
		private readonly EventsContainer<MarketDepth> _newMarketDepthsEvent;
		private readonly EventsContainer<Portfolio> _newPortfoliosEvent;
		private readonly EventsContainer<Portfolio> _portfoliosChangedEvent;
		private readonly EventsContainer<Position> _newPositionsEvent;
		private readonly EventsContainer<Position> _positionsChangedEvent;

		private readonly SubscriptionManager _subscriptionManager;

		private Timer _marketTimeChangedTimer;

		private Exception _licenseError;

		/// <summary>
		/// Инициализировать <see cref="BaseTrader"/>.
		/// </summary>
		/// <param name="platform">Разрядность процесса, в котором может работать шлюз.</param>
		/// <param name="checkLicense">Проверять наличие лицензии.</param>
		protected BaseTrader(Platforms platform = Platforms.AnyCPU, bool checkLicense = true)
		{
			Platform = platform;

			_subscriptionManager = new SubscriptionManager(this);

			TransactionIdGenerator = new IncrementTransactionIdGenerator();
			SecurityIdGenerator = new SecurityIdGenerator();
			SecurityClassInfo = new Dictionary<string, RefPair<SecurityTypes, ExchangeBoard>>();

			_newSecuritiesEvent = new EventsContainer<Security>(RaiseProcessDataError);
			_securitiesChangedEvent = new EventsContainer<Security>(RaiseProcessDataError);
			_newPortfoliosEvent = new EventsContainer<Portfolio>(RaiseProcessDataError);
			_portfoliosChangedEvent = new EventsContainer<Portfolio>(RaiseProcessDataError);
			_newPositionsEvent = new EventsContainer<Position>(RaiseProcessDataError);
			_positionsChangedEvent = new EventsContainer<Position>(RaiseProcessDataError);
			_newTradesEvent = new EventsContainer<Trade>(RaiseProcessDataError);
			_ordersRegisterFailedEvent = new EventsContainer<OrderFail>(RaiseProcessDataError);
			_ordersCancelFailedEvent = new EventsContainer<OrderFail>(RaiseProcessDataError);
			_stopOrdersRegisterFailedEvent = new EventsContainer<OrderFail>(RaiseProcessDataError);
			_stopOrdersCancelFailedEvent = new EventsContainer<OrderFail>(RaiseProcessDataError);
			_newOrdersEvent = new EventsContainer<Order>(RaiseProcessDataError);
			_ordersChangedEvent = new EventsContainer<Order>(RaiseProcessDataError);
			_newStopOrdersEvent = new EventsContainer<Order>(RaiseProcessDataError);
			_stopOrdersChangedEvent = new EventsContainer<Order>(RaiseProcessDataError);
			_newMyTradesEvent = new EventsContainer<MyTrade>(RaiseProcessDataError);
			_newMarketDepthsEvent = new EventsContainer<MarketDepth>(RaiseProcessDataError);
			_marketDepthsChangedEvent = new EventsContainer<MarketDepth>(RaiseProcessDataError);

			_reConnectionManager = new ReConnectionManager(this, RaiseConnectionRestored, RaiseConnectionAttemptError, RaiseExportTimeOut);

			TradesKeepTime = TimeSpan.FromDays(2);

			if (!checkLicense)
				return;

			_licenseError = this.StartValidation(() =>
			{
				try
				{
					Disconnect();
					RaiseConnectionError(new InvalidOperationException("Соединение разорвано по причине неудачи проверки лицензии."));
				}
				catch (Exception ex)
				{
					RaiseProcessDataError(ex);
				}
			});

			UpdateSecurityOnEachEvent = true;

			MemoryStatistics.Instance.Traders.Add(this);
		}

		/// <summary>
		/// Разрядность процесса, в котором может работать шлюз.
		/// </summary>
		public Platforms Platform { get; protected set; }

		/// <summary>
		/// Настройки переподсоединения <see cref="ITrader"/> к торговой системе.
		/// </summary>
		public ReConnectionSettings ReConnectionSettings
		{
			get { return _reConnectionManager.Settings; }
		}

		private IEntityFactory _entityFactory = Algo.EntityFactory.Instance;

		/// <summary>
		/// Фабрика бизнес-сущностей (<see cref="Security"/>, <see cref="Order"/> и т.д.).
		/// </summary>
		public IEntityFactory EntityFactory
		{
			get { return _entityFactory; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_entityFactory = value;
			}
		}

		/// <summary>
		/// Время хранения тиковых сделок в памяти.
		/// По-умолчанию равно 2-ум дням. Если значение установлено в <see cref="TimeSpan.Zero"/>, то сделки не будут удаляться.
		/// </summary>
		public TimeSpan TradesKeepTime
		{
			get { return _cache.TradesKeepTime; }
			set { _cache.TradesKeepTime = value; }
		}

		private TimeSpan _marketTimeChangedInterval = TimeSpan.FromMilliseconds(10);

		/// <summary>
		/// Интервал вызова события <see cref="MarketTimeChanged"/>.
		/// </summary>
		/// <remarks>
		/// Значение по умолчанию равно 10 миллисекундам.
		/// </remarks>
		public TimeSpan MarketTimeChangedInterval
		{
			get { return _marketTimeChangedInterval; }
			set
			{
				if (value <= TimeSpan.Zero)
					throw new ArgumentOutOfRangeException("value", value, "Интервал вызова события MarketTimeChanged не может быть меньше или равен нулю.");

				_marketTimeChangedInterval = value;
			}
		}

		/// <summary>
		/// Текстовое описание шлюза.
		/// </summary>
		public abstract string DisplayName { get; }

		/// <summary>
		/// Событие появления собственных новых сделок.
		/// </summary>
		public virtual event Action<IEnumerable<MyTrade>> NewMyTrades
		{
			add { _newMyTradesEvent.Event += value; }
			remove { _newMyTradesEvent.Event -= value; }
		}

		/// <summary>
		/// Событие появления всех новых сделок.
		/// </summary>
		public virtual event Action<IEnumerable<Trade>> NewTrades
		{
			add { _newTradesEvent.Event += value; }
			remove { _newTradesEvent.Event -= value; }
		}

		/// <summary>
		/// Событие появления новых заявок.
		/// </summary>
		public virtual event Action<IEnumerable<Order>> NewOrders
		{
			add { _newOrdersEvent.Event += value; }
			remove { _newOrdersEvent.Event -= value; }
		}

		/// <summary>
		/// Событие появления новых стоп-заявок.
		/// </summary>
		public virtual event Action<IEnumerable<Order>> NewStopOrders
		{
			add { _newStopOrdersEvent.Event += value; }
			remove { _newStopOrdersEvent.Event -= value; }
		}

		/// <summary>
		/// Событие изменения состояния заявок (снята, удовлетворена).
		/// </summary>
		public virtual event Action<IEnumerable<Order>> OrdersChanged
		{
			add { _ordersChangedEvent.Event += value; }
			remove { _ordersChangedEvent.Event -= value; }
		}

		/// <summary>
		/// Событие изменения стоп-заявок.
		/// </summary>
		public virtual event Action<IEnumerable<Order>> StopOrdersChanged
		{
			add { _stopOrdersChangedEvent.Event += value; }
			remove { _stopOrdersChangedEvent.Event -= value; }
		}

		/// <summary>
		/// Событие об ошибках, связанных с регистрацией заявок.
		/// </summary>
		public virtual event Action<IEnumerable<OrderFail>> OrdersRegisterFailed
		{
			add { _ordersRegisterFailedEvent.Event += value; }
			remove { _ordersRegisterFailedEvent.Event -= value; }
		}

		/// <summary>
		/// Событие об ошибках, связанных со снятием заявок.
		/// </summary>
		public virtual event Action<IEnumerable<OrderFail>> OrdersCancelFailed
		{
			add { _ordersCancelFailedEvent.Event += value; }
			remove { _ordersCancelFailedEvent.Event -= value; }
		}

		/// <summary>
		/// Событие об ошибках, связанных с регистрацией стоп-заявок.
		/// </summary>
		public virtual event Action<IEnumerable<OrderFail>> StopOrdersRegisterFailed
		{
			add { _stopOrdersRegisterFailedEvent.Event += value; }
			remove { _stopOrdersRegisterFailedEvent.Event -= value; }
		}

		/// <summary>
		/// Событие об ошибках, связанных со снятием стоп-заявок.
		/// </summary>
		public virtual event Action<IEnumerable<OrderFail>> StopOrdersCancelFailed
		{
			add { _stopOrdersCancelFailedEvent.Event += value; }
			remove { _stopOrdersCancelFailedEvent.Event -= value; }
		}

		/// <summary>
		/// Событие появления новых инструментов.
		/// </summary>
		public virtual event Action<IEnumerable<Security>> NewSecurities
		{
			add { _newSecuritiesEvent.Event += value; }
			remove { _newSecuritiesEvent.Event -= value; }
		}

		/// <summary>
		/// Событие изменения параметров инструментов.
		/// </summary>
		public virtual event Action<IEnumerable<Security>> SecuritiesChanged
		{
			add { _securitiesChangedEvent.Event += value; }
			remove { _securitiesChangedEvent.Event -= value; }
		}

		/// <summary>
		/// Событие появления новых портфелей.
		/// </summary>
		public virtual event Action<IEnumerable<Portfolio>> NewPortfolios
		{
			add { _newPortfoliosEvent.Event += value; }
			remove { _newPortfoliosEvent.Event -= value; }
		}

		/// <summary>
		/// Событие изменения параметров портфелей.
		/// </summary>
		public virtual event Action<IEnumerable<Portfolio>> PortfoliosChanged
		{
			add { _portfoliosChangedEvent.Event += value; }
			remove { _portfoliosChangedEvent.Event -= value; }
		}

		/// <summary>
		/// Событие появления новых позиций.
		/// </summary>
		public virtual event Action<IEnumerable<Position>> NewPositions
		{
			add { _newPositionsEvent.Event += value; }
			remove { _newPositionsEvent.Event -= value; }
		}

		/// <summary>
		/// Событие изменения параметров позиций.
		/// </summary>
		public virtual event Action<IEnumerable<Position>> PositionsChanged
		{
			add { _positionsChangedEvent.Event += value; }
			remove { _positionsChangedEvent.Event -= value; }
		}

		/// <summary>
		/// Событие появления новых стаканов с котировками.
		/// </summary>
		public event Action<IEnumerable<MarketDepth>> NewMarketDepths
		{
			add { _newMarketDepthsEvent.Event += value; }
			remove { _newMarketDepthsEvent.Event -= value; }
		}

		/// <summary>
		/// Событие изменения стаканов с котировками.
		/// </summary>
		public virtual event Action<IEnumerable<MarketDepth>> MarketDepthsChanged
		{
			add { _marketDepthsChangedEvent.Event += value; }
			remove { _marketDepthsChangedEvent.Event -= value; }
		}

		/// <summary>
		/// Событие появления новых записей в логе заявок.
		/// </summary>
		public event Action<IEnumerable<OrderLogItem>> NewOrderLogItems;

		/// <summary>
		/// Событие, сигнализирующее об изменении текущего времени на биржевых площадках <see cref="ITrader.ExchangeBoards"/>.
		/// Передается разница во времени, прошедшее с последнего вызова события. Первый раз событие передает значение <see cref="TimeSpan.Zero"/>.
		/// </summary>
		public virtual event Action<TimeSpan> MarketTimeChanged;

		/// <summary>
		/// Событие успешного подключения.
		/// </summary>
		public virtual event Action Connected;

		/// <summary>
		/// Событие успешного отключения.
		/// </summary>
		public virtual event Action Disconnected;

		/// <summary>
		/// Событие ошибки подключения (например, соединения было разорвано).
		/// </summary>
		public virtual event Action<Exception> ConnectionError;

		/// <summary>
		/// Событие, сигнализирующее об ошибке при получении или обработке новых данных с сервера.
		/// </summary>
		public virtual event Action<Exception> ProcessDataError;

		/// <summary>
		/// Событие, сигнализирующее об ошибке при получении или обработке новых данных с сервера.
		/// </summary>
		public virtual event Action NewDataExported;

		private TransactionIdGenerator _transactionIdGenerator;

		/// <summary>
		/// Генератор идентификаторов транзакций.
		/// </summary>
		public TransactionIdGenerator TransactionIdGenerator
		{
			get { return _transactionIdGenerator; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_transactionIdGenerator = value;
			}
		}

		private SecurityIdGenerator _securityIdGenerator;

		/// <summary>
		/// Генератор идентификаторов инструментов <see cref="Security.Id"/>.
		/// </summary>
		public SecurityIdGenerator SecurityIdGenerator
		{
			get { return _securityIdGenerator; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_securityIdGenerator = value;
			}
		}

		private RefPair<SecurityTypes, ExchangeBoard> _defaultSecurityClassInfo = new RefPair<SecurityTypes, ExchangeBoard>(SecurityTypes.Stock, ExchangeBoard.Micex);

		/// <summary>
		/// Описания класса инструмента по умолчанию.
		/// </summary>
		public RefPair<SecurityTypes, ExchangeBoard> DefaultSecurityClassInfo
		{
			get { return _defaultSecurityClassInfo; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_defaultSecurityClassInfo = value;
			}
		}

		/// <summary>
		/// Описание классов инструментов, в зависимости от которых будут проставляться параметры в <see cref="Security.Type"/> и <see cref="Security.ExchangeBoard"/>.
		/// Если информация по классу отсутствует, то будет поставлены значения <see cref="SecurityTypes.Stock"/> и <see cref="ExchangeBoard.Micex"/> соответственно.
		/// </summary>
		public IDictionary<string, RefPair<SecurityTypes, ExchangeBoard>> SecurityClassInfo { get; private set; }

		private readonly CachedSynchronizedSet<ExchangeBoard> _exchangeBoards = new CachedSynchronizedSet<ExchangeBoard>();

		/// <summary>
		/// Список всех биржевых площадок, для которых загружены инструменты <see cref="ITrader.Securities"/>.
		/// </summary>
		public IEnumerable<ExchangeBoard> ExchangeBoards
		{
			get { return _exchangeBoards.Cache; }
		}

		private readonly CachedSynchronizedDictionary<string, Security> _securities = new CachedSynchronizedDictionary<string, Security>();

		/// <summary>
		/// Список всех загруженных инструментов.
		/// Вызывать необходимо после того, как пришло событие <see cref="ITrader.NewSecurities" />. Иначе будет возвращено постое множество.
		/// </summary>
		public virtual IEnumerable<Security> Securities
		{
			get { return _securities.CachedValues; }
		}

		/// <summary>
		/// Получить биржевое время.
		/// </summary>
		/// <param name="exchange">Биржа.</param>
		/// <returns>Биржевое время.</returns>
		public virtual DateTime GetMarketTime(Exchange exchange)
		{
			if (exchange == null)
				throw new ArgumentNullException("exchange");

			return exchange.ToExchangeTime(LoggingHelper.Now);
		}

		/// <summary>
		/// Получить все заявки.
		/// </summary>
		public virtual IEnumerable<Order> Orders
		{
			get { return _cache.Orders; }
		}

		/// <summary>
		/// Получить все стоп-заявки.
		/// </summary>
		public virtual IEnumerable<Order> StopOrders
		{
			get { return Orders.Where(o => o.Type == OrderTypes.Conditional); }
		}

		private readonly SynchronizedList<OrderFail> _orderRegisterFails = new SynchronizedList<OrderFail>();

		/// <summary>
		/// Получить все ошибки при регистрации заявок.
		/// </summary>
		public virtual IEnumerable<OrderFail> OrderRegisterFails
		{
			get { return _orderRegisterFails.SyncGet(c => c.ToArray()); }
		}

		private readonly SynchronizedList<OrderFail> _orderCancelFails = new SynchronizedList<OrderFail>();

		/// <summary>
		/// Получить все ошибки при снятии заявок.
		/// </summary>
		public virtual IEnumerable<OrderFail> OrderCancelFails
		{
			get { return _orderCancelFails.SyncGet(c => c.ToArray()); }
		}

		/// <summary>
		/// Получить все сделки.
		/// </summary>
		public virtual IEnumerable<Trade> Trades
		{
			get { return _cache.Trades; }
		}

		/// <summary>
		/// Получить все собственные сделки.
		/// </summary>
		public virtual IEnumerable<MyTrade> MyTrades
		{
			get { return _cache.MyTrades; }
		}

		private readonly CachedSynchronizedDictionary<string, Portfolio> _portfolios = new CachedSynchronizedDictionary<string, Portfolio>();

		/// <summary>
		/// Получить все портфели.
		/// </summary>
		public virtual IEnumerable<Portfolio> Portfolios
		{
			get { return _portfolios.CachedValues; }
		}

		private readonly CachedSynchronizedDictionary<Tuple<Portfolio, Security, string>, Position> _positions = new CachedSynchronizedDictionary<Tuple<Portfolio, Security, string>, Position>();

		/// <summary>
		/// Получить все позиции.
		/// </summary>
		public virtual IEnumerable<Position> Positions
		{
			get { return _positions.CachedValues; }
		}

		private bool _isConnected;

		/// <summary>
		/// Проверить соединение.
		/// </summary>
		public virtual bool IsConnected
		{
			get { return _isConnected; }
			protected set
			{
				if (IsConnected == value)
					return;

				_isConnected = value;

				if (value)
					RaiseConnected();
				else
					RaiseDisconnected();
			}
		}

		/// <summary>
		/// Запущен ли экспорт. Экспорт запускается через метод <see cref="StartExport"/>.
		/// </summary>
		public virtual bool IsExportStarted { get; protected set; }

		private bool _isSupportAtomicReRegister = true;

		/// <summary>
		/// Поддерживается ли перерегистрация заявок через метод <see cref="ITrader.ReRegisterOrder(StockSharp.BusinessEntities.Order,StockSharp.BusinessEntities.Order)"/>
		/// в виде одной транзакции. По-умолчанию включено.
		/// </summary>
		public virtual bool IsSupportAtomicReRegister
		{
			get { return _isSupportAtomicReRegister; }
			protected set { _isSupportAtomicReRegister = value; }
		}

		/// <summary>
		/// Список всех инструментов, зарегистрированных через <see cref="ITrader.RegisterSecurity"/>.
		/// </summary>
		public IEnumerable<Security> RegisteredSecurities
		{
			get { return _subscriptionManager.RegisteredSecurities; }
		}

		/// <summary>
		/// Список всех инструментов, зарегистрированных через <see cref="ITrader.RegisterMarketDepth"/>.
		/// </summary>
		public IEnumerable<Security> RegisteredMarketDepths
		{
			get { return _subscriptionManager.RegisteredMarketDepths; }
		}

		/// <summary>
		/// Список всех инструментов, зарегистрированных через <see cref="ITrader.RegisterTrades"/>.
		/// </summary>
		public IEnumerable<Security> RegisteredTrades
		{
			get { return _subscriptionManager.RegisteredTrades; }
		}

		/// <summary>
		/// Список всех инструментов, зарегистрированных через <see cref="ITrader.RegisterOrderLog"/>.
		/// </summary>
		public IEnumerable<Security> RegisteredOrderLogs
		{
			get { return _subscriptionManager.RegisteredOrderLogs; }
		}

		/// <summary>
		/// Список всех портфелей, зарегистрированных через <see cref="ITrader.RegisterPortfolio"/>.
		/// </summary>
		public IEnumerable<Portfolio> RegisteredPortfolios
		{
			get { return _subscriptionManager.RegisteredPortfolios; }
		}

		/// <summary>
		/// Использовать лог заявок (orders log) для создания стаканов. По-умолчанию выключено.
		/// </summary>
		public virtual bool CreateDepthFromOrdersLog { get; set; }

		/// <summary>
		/// Использовать лог заявок (orders log) для создания тиковых сделок. По-умолчанию выключено.
		/// </summary>
		public virtual bool CreateTradesFromOrdersLog { get; set; }

		/// <summary>
		/// Обновлять <see cref="Security.LastTrade"/>, <see cref="Security.BestBid"/>, <see cref="Security.BestAsk"/> на каждом обновлении стакана и/или сделок.
		/// По умолчанию true.
		/// </summary>
		public bool UpdateSecurityOnEachEvent { get; set; }

		/// <summary>
		/// Подключиться к торговой системе.
		/// </summary>
		public void Connect()
		{
			try
			{
				if (!Platform.IsCompatible())
					throw new InvalidOperationException("Текущая разрядность процесса не совместима со шлюзом {0}. Необходимо перекомпилировать под {1}.".Put(Name, Platform));

				if (_licenseError != null)
				{
					RaiseProcessDataError(_licenseError);
					_licenseError = null;
				}

				_reConnectionManager.Connect(() => RaiseConnectionError(new TimeoutException("Соединение не было установлено в отведенный отрезок времени.")));
				OnConnect();
			}
			catch (Exception ex)
			{
				RaiseConnectionError(ex);
			}
		}

		/// <summary>
		/// Подключиться к торговой системе.
		/// </summary>
		protected virtual void OnConnect()
		{
			IsConnected = true;
		}

		/// <summary>
		/// Отключиться от торговой системы.
		/// </summary>
		public void Disconnect()
		{
			_reConnectionManager.Disconnect(() => RaiseConnectionError(new TimeoutException("Соединение не было разорвано в отведенный отрезок времени.")));
			OnDisconnect();
		}

		/// <summary>
		/// Отключиться от торговой системы.
		/// </summary>
		protected virtual void OnDisconnect()
		{
			IsConnected = false;
		}

		private void SafeDisconnect()
		{
			try
			{
				//if (IsConnected)
				Disconnect();
			}
			catch (Exception ex)
			{
				RaiseConnectionError(ex);
			}
		}

		/// <summary>
		/// Переподключиться к торговой системе.
		/// </summary>
		public virtual void Reconnect()
		{
			SafeDisconnect();
			Connect();
		}

		/// <summary>
		/// Получить позицию по портфелю и инструменту.
		/// </summary>
		/// <param name="portfolio">Портфель, по которому нужно найти позицию.</param>
		/// <param name="security">Инструмент, по которому нужно найти позицию.</param>
		/// <param name="depoName">Название депозитария, где находится физически ценная бумага.
		/// По-умолчанию передается пустая строка, что означает суммарную позицию по всем депозитариям.</param>
		/// <returns>Позиция.</returns>
		public virtual Position GetPosition(Portfolio portfolio, Security security, string depoName = "")
		{
			if (portfolio == null)
				throw new ArgumentNullException("portfolio");

			if (security == null)
				throw new ArgumentNullException("security");

			Position position;

			var isNew = false;

			lock (_positions.SyncRoot)
			{
				if(depoName == null)
					depoName = String.Empty;

				var key = new Tuple<Portfolio, Security, string>(portfolio, security, depoName);

				if (!_positions.TryGetValue(key, out position))
				{
					isNew = true;

					position = EntityFactory.CreatePosition(portfolio, security);
					position.DepoName = depoName;
					_positions.Add(key, position);
				}
			}

			if (isNew)
				RaiseNewPositions(new[] { position });

			return position;
		}

		/// <summary>
		/// Получить стакан котировок.
		/// </summary>
		/// <param name="security">Инструмент, по которому нужно получить стакан.</param>
		/// <returns>Стакан котировок.</returns>
		public virtual MarketDepth GetMarketDepth(Security security)
		{
			if (security == null)
				throw new ArgumentNullException("security");

			MarketDepth depth;

			var isNew = false;

			lock (_securities.SyncRoot)
			{
				if (!_marketDepths.TryGetValue(security, out depth))
				{
					isNew = true;

					depth = EntityFactory.CreateMarketDepth(security);

					// стакан из лога заявок бесконечен
					if (CreateDepthFromOrdersLog)
						depth.MaxDepth = int.MaxValue;

					_marketDepths.Add(security, depth);
				}
			}

			if (isNew)
				RaiseNewMarketDepths(new[] { depth });

			return depth;
		}

		/// <summary>
		/// Зарегистрировать заявку на бирже.
		/// </summary>
		/// <param name="order">Заявка, содержащая информацию для регистрации.</param>
		public void RegisterOrder(Order order)
		{
			try
			{
				order.CheckOnNew();
				InitNewOrder(order);

				this.AddOrderInfoLog(order, "RegisterOrder");
				this.ChangeContinuousSecurity(order);
				OnRegisterOrder(order);
			}
			catch (Exception ex)
			{
				RaiseOrderFailed(order, ex);

				// mika
				// котирование ожидает ошибки через событие
				//throw;
			}
		}

		/// <summary>
		/// Перерегистрировать заявку на бирже.
		/// </summary>
		/// <param name="oldOrder">Заявка, которую нужно снять и на основе нее зарегистрировать новую.</param>
		/// <param name="price">Цена новой заявки.</param>
		/// <param name="volume">Объем новой заявки.</param>
		/// <returns>Новая заявка.</returns>
		/// <remarks>
		/// Если объём не задан, меняется только цена.
		/// </remarks>
		public virtual Order ReRegisterOrder(Order oldOrder, decimal price, decimal volume = 0)
		{
			if (oldOrder == null)
				throw new ArgumentNullException("oldOrder");

			var newOrder = oldOrder.ReRegisterClone(price, volume);
			
			ReRegisterOrder(oldOrder, newOrder);
			return newOrder;
		}

		/// <summary>
		/// Перерегистрировать заявку на бирже.
		/// </summary>
		/// <param name="oldOrder">Заявка, которую нужно снять.</param>
		/// <param name="newOrder">Новая заявка, которую нужно зарегистрировать.</param>
		public virtual void ReRegisterOrder(Order oldOrder, Order newOrder)
		{
			if (oldOrder == null)
				throw new ArgumentNullException("oldOrder");

			if (newOrder == null)
				throw new ArgumentNullException("newOrder");

			try
			{
				if (oldOrder.Security != newOrder.Security)
					throw new ArgumentException("Новая заявка регистрируется по инструменту {0}. Старая зарегистрирована по {1}.".Put(newOrder.Security.Id, oldOrder.Security.Id), "newOrder");

				if (oldOrder.Type == OrderTypes.Conditional)
				{
					CancelOrder(oldOrder);
					RegisterOrder(newOrder);
				}
				else
				{
					oldOrder.CheckOnOld();
					newOrder.CheckOnNew(false);

					if (oldOrder.Comment.IsEmpty())
						oldOrder.Comment = newOrder.Comment;

					OnReRegisterOrder(oldOrder, newOrder);
				}
			}
			catch (Exception ex)
			{
				RaiseOrderFailed(newOrder, ex);

				// mika
				// котирование ожидает ошибки через событие
				//throw;
			}
		}

		/// <summary>
		/// Перерегистрировать пару заявок на бирже.
		/// </summary>
		/// <param name="oldOrder1">Первая заявка, которую нужно снять.</param>
		/// <param name="newOrder1">Первая новая заявка, которую нужно зарегистрировать.</param>
		/// <param name="oldOrder2">Вторая заявка, которую нужно снять.</param>
		/// <param name="newOrder2">Вторая новая заявка, которую нужно зарегистрировать.</param>
		public virtual void ReRegisterOrderPair(Order oldOrder1, Order newOrder1, Order oldOrder2, Order newOrder2)
		{
			if (oldOrder1 == null)
				throw new ArgumentNullException("oldOrder1");

			if (newOrder1 == null)
				throw new ArgumentNullException("newOrder1");

			if (oldOrder2 == null)
				throw new ArgumentNullException("oldOrder2");

			if (newOrder2 == null)
				throw new ArgumentNullException("newOrder2");

			try
			{
				if (oldOrder1.Security != newOrder1.Security)
					throw new ArgumentException("Первая новая заявка регистрируется по инструменту {0}. Старая зарегистрирована по {1}.".Put(newOrder1.Security.Id, oldOrder1.Security.Id), "newOrder1");

				if (oldOrder2.Security != newOrder2.Security)
					throw new ArgumentException("Вторая новая заявка регистрируется по инструменту {0}. Старая зарегистрирована по {1}.".Put(newOrder2.Security.Id, oldOrder2.Security.Id), "newOrder2");

				if (oldOrder1.Type == OrderTypes.Conditional || oldOrder2.Type == OrderTypes.Conditional)
				{
					CancelOrder(oldOrder1);
					RegisterOrder(newOrder1);

					CancelOrder(oldOrder2);
					RegisterOrder(newOrder2);
				}
				else
				{
					oldOrder1.CheckOnOld();
					newOrder1.CheckOnNew(false);

					oldOrder2.CheckOnOld();
					newOrder2.CheckOnNew(false);

					if (oldOrder1.Comment.IsEmpty())
						oldOrder1.Comment = newOrder1.Comment;

					if (oldOrder2.Comment.IsEmpty())
						oldOrder2.Comment = newOrder2.Comment;

					OnReRegisterOrderPair(oldOrder1, newOrder1, oldOrder2, newOrder2);
				}
			}
			catch (Exception ex)
			{
				RaiseOrderFailed(newOrder1, ex);
				RaiseOrderFailed(newOrder2, ex);

				// mika
				// котирование ожидает ошибки через событие
				//throw;
			}
		}

		/// <summary>
		/// Отменить заявку на бирже.
		/// </summary>
		/// <param name="order">Заявка, которую нужно отменять.</param>
		public virtual void CancelOrder(Order order)
		{
			order.CheckOnOld();
			order.InitLatencyMonitoring(false);

			this.AddOrderInfoLog(order, "CancelOrder");

			OnCancelOrder(order);
		}

		/// <summary>
		/// Инициализировать новую заявку номером транзакции, информацией о шлюзе и т.д.
		/// </summary>
		/// <param name="order">Новая заявка.</param>
		protected void InitNewOrder(Order order)
		{
			order.InitOrder(this, TransactionIdGenerator);
			AddOrderByTransactionId(order);
		}

		/// <summary>
		/// Зарегистрировать заявку на бирже.
		/// </summary>
		/// <param name="order">Заявка, содержащая информацию для регистрации.</param>
		protected abstract void OnRegisterOrder(Order order);

		/// <summary>
		/// Перерегистрировать заявку на бирже.
		/// </summary>
		/// <param name="oldOrder">Заявка, которую нужно снять.</param>
		/// <param name="newOrder">Новая заявка, которую нужно зарегистрировать.</param>
		protected virtual void OnReRegisterOrder(Order oldOrder, Order newOrder)
		{
			CancelOrder(oldOrder);
			RegisterOrder(newOrder);
		}

		/// <summary>
		/// Перерегистрировать пару заявок на бирже.
		/// </summary>
		/// <param name="oldOrder1">Первая заявка, которую нужно снять.</param>
		/// <param name="newOrder1">Первая новая заявка, которую нужно зарегистрировать.</param>
		/// <param name="oldOrder2">Вторая заявка, которую нужно снять.</param>
		/// <param name="newOrder2">Вторая новая заявка, которую нужно зарегистрировать.</param>
		protected virtual void OnReRegisterOrderPair(Order oldOrder1, Order newOrder1, Order oldOrder2, Order newOrder2)
		{
			CancelOrder(oldOrder1);
			RegisterOrder(newOrder1);

			CancelOrder(oldOrder2);
			RegisterOrder(newOrder2);
		}

		/// <summary>
		/// Отменить заявку на бирже.
		/// </summary>
		/// <param name="order">Заявка, которую нужно отменять.</param>
		protected abstract void OnCancelOrder(Order order);

		/// <summary>
		/// Отменить группу заявок на бирже по фильтру.
		/// </summary>
		/// <param name="isStopOrder">True, если нужно отменить только стоп-заявки, false - если только обычный и null - если оба типа.</param>
		/// <param name="portfolio">Портфель. Если значение равно null, то портфель не попадает в фильтр снятия заявок.</param>
		/// <param name="direction">Направление заявки. Если значение равно null, то направление не попадает в фильтр снятия заявок.</param>
		/// <param name="classCode">Код класса. Если переданная строка пустая, то код не попадает в фильтр снятия заявок.</param>
		/// <param name="security">Инструмент. Если значение равно null, то инструмент не попадает в фильтр снятия заявок.</param>
		public virtual void CancelOrders(bool? isStopOrder = null, Portfolio portfolio = null, OrderDirections? direction = null, string classCode = null, Security security = null)
		{
			this.CancelOrders(Orders, isStopOrder, portfolio, direction, classCode, security);
		}

		/// <summary>
		/// Начать получать новую информацию (например, <see cref="Security.LastTrade"/> или <see cref="Security.BestBid"/>) по инструменту.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать новую информацию.</param>
		public void RegisterSecurity(Security security)
		{
			_subscriptionManager.RegisterSecurity(security);
		}

		/// <summary>
		/// Начать получать новую информацию (например, <see cref="Security.LastTrade"/> или <see cref="Security.BestBid"/>) по инструменту.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать новую информацию.</param>
		protected virtual void OnRegisterSecurity(Security security)
		{
		}

		/// <summary>
		/// Остановить получение новой информации.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение новой информации.</param>
		public void UnRegisterSecurity(Security security)
		{
			_subscriptionManager.UnRegisterSecurity(security);
		}

		/// <summary>
		/// Остановить получение новой информации.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение новой информации.</param>
		protected virtual void OnUnRegisterSecurity(Security security)
		{
		}

		/// <summary>
		/// Начать получать котировки (стакан) по инструменту.
		/// Значение котировок можно получить через метод <see cref="ITrader.GetMarketDepth"/>.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать котировки.</param>
		public void RegisterMarketDepth(Security security)
		{
			_subscriptionManager.RegisterMarketDepth(security);
		}

		/// <summary>
		/// Начать получать котировки (стакан) по инструменту.
		/// Значение котировок можно получить через метод <see cref="ITrader.GetMarketDepth"/>.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать котировки.</param>
		protected virtual void OnRegisterMarketDepth(Security security)
		{
		}

		/// <summary>
		/// Остановить получение котировок по инструменту.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение котировок.</param>
		public void UnRegisterMarketDepth(Security security)
		{
			_subscriptionManager.UnRegisterMarketDepth(security);
		}

		/// <summary>
		/// Остановить получение котировок по инструменту.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение котировок.</param>
		protected virtual void OnUnRegisterMarketDepth(Security security)
		{
		}

		/// <summary>
		/// Начать получать сделки (тиковые данные) по инструменту. Новые сделки будут приходить через
		/// событие <see cref="ITrader.NewTrades"/>.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать сделки.</param>
		public void RegisterTrades(Security security)
		{
			_subscriptionManager.RegisterTrades(security);
		}

		/// <summary>
		/// Начать получать сделки (тиковые данные) по инструменту. Новые сделки будут приходить через
		/// событие <see cref="ITrader.NewTrades"/>.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать сделки.</param>
		protected virtual void OnRegisterTrades(Security security)
		{
		}

		/// <summary>
		/// Остановить получение сделок (тиковые данные) по инструменту.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение сделок.</param>
		public void UnRegisterTrades(Security security)
		{
			_subscriptionManager.UnRegisterTrades(security);
		}

		/// <summary>
		/// Остановить получение сделок (тиковые данные) по инструменту.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение сделок.</param>
		protected virtual void OnUnRegisterTrades(Security security)
		{
		}

		/// <summary>
		/// Начать получать новую информацию по портфелю.
		/// </summary>
		/// <param name="portfolio">Портфель, по которому необходимо начать получать новую информацию.</param>
		public void RegisterPortfolio(Portfolio portfolio)
		{
			_subscriptionManager.RegisterPortfolio(portfolio);
		}

		/// <summary>
		/// Начать получать новую информацию по портфелю.
		/// </summary>
		/// <param name="portfolio">Портфель, по которому необходимо начать получать новую информацию.</param>
		protected virtual void OnRegisterPortfolio(Portfolio portfolio)
		{
		}

		/// <summary>
		/// Остановить получение новой информации по портфелю.
		/// </summary>
		/// <param name="portfolio">Портфель, по которому необходимо остановить получение новой информации.</param>
		public void UnRegisterPortfolio(Portfolio portfolio)
		{
			_subscriptionManager.UnRegisterPortfolio(portfolio);
		}

		/// <summary>
		/// Остановить получение новой информации по портфелю.
		/// </summary>
		/// <param name="portfolio">Портфель, по которому необходимо остановить получение новой информации.</param>
		protected virtual void OnUnRegisterPortfolio(Portfolio portfolio)
		{
		}

		/// <summary>
		/// Начать получать лог заявок для инструмента.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать лог заявок.</param>
		public void RegisterOrderLog(Security security)
		{
			_subscriptionManager.RegisterOrderLog(security);
		}

		/// <summary>
		/// Начать получать лог заявок для инструмента.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать лог заявок.</param>
		protected virtual void OnRegisterOrderLog(Security security)
		{
		}

		/// <summary>
		/// Остановить получение лога заявок для инструмента.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение лога заявок.</param>
		public void UnRegisterOrderLog(Security security)
		{
			_subscriptionManager.UnRegisterOrderLog(security);
		}

		/// <summary>
		/// Остановить получение лога заявок для инструмента.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение лога заявок.</param>
		protected virtual void OnUnRegisterOrderLog(Security security)
		{
		}

		/// <summary>
		/// Поддерживает ли шлюз инструмент.
		/// </summary>
		/// <param name="security">Инструмент.</param>
		/// <returns>True, если шлюз поддерживает инструмент, иначе, false.</returns>
		public virtual bool IsSupport(Security security)
		{
			if (security == null)
				throw new ArgumentNullException("security");

			return Securities.Contains(security);
		}

		/// <summary>
		/// Обработать поступившие из шлюза данные. Всю обработку полученных данных необходимо провести в функции, передающаяся в качестве аргумента handler.
		/// По окончанию выполнения будут вызваны событие о новых изменения (например, таких как <see cref="NewSecurities"/> или <see cref="OrdersChanged"/>).
		/// </summary>
		/// <param name="handler">Функция, в которой необходимо обработать поступившие данные.</param>
		protected void ProcessEvents(Action handler)
		{
			if (handler == null)
				throw new ArgumentNullException("handler");

			EventsContainer.BeginSuspend();

			try
			{
				RaiseNewDataExported();
				handler();
			}
			catch (Exception ex)
			{
				RaiseProcessDataError(ex);
			}
			finally
			{
				try
				{
					EventsContainer.EndSuspend();
				}
				catch (Exception ex)
				{
					RaiseProcessDataError(ex);
				}
			}
		}

		/// <summary>
		/// Вызвать событие <see cref="OrdersRegisterFailed"/>, <see cref="OrdersCancelFailed"/>, <see cref="StopOrdersRegisterFailed"/> или <see cref="StopOrdersRegisterFailed"/> 
		/// в зависимости от типа и биржевого номера заявки <see cref="OrderFail.Order"/>.
		/// </summary>
		/// <param name="order">Заявка, которая не была принята торговой системой.</param>
		/// <param name="exception">Системная информация об ошибке.</param>
		/// <param name="changeState">Можно ли перевести состояние заявки в <see cref="OrderStates.Failed"/>. По-умолчанию равно true.</param>
		protected void RaiseOrderFailed(Order order, Exception exception, bool changeState = true)
		{
			if (order == null)
				throw new ArgumentNullException("order");

			if (exception == null)
				throw new ArgumentNullException("exception");

			//В состояние Failed можно переводить только не дошедшие до биржи заявки
			if (changeState && order.State == OrderStates.None)
				order.State = OrderStates.Failed;

			order.Messages.Add(exception.Message);

			var fail = EntityFactory.CreateOrderFail(order, exception);

			if (order.Id == 0)
			{
				_orderRegisterFails.Add(fail);
			}
			else
			{
				_orderCancelFails.Add(fail);
			}

			RaiseOrderFailed(fail);
		}

		/// <summary>
		/// Вызвать событие <see cref="OrdersRegisterFailed"/>, <see cref="OrdersCancelFailed"/>, <see cref="StopOrdersRegisterFailed"/> или <see cref="StopOrdersRegisterFailed"/> 
		/// в зависимости от типа и биржевого номера заявки <see cref="OrderFail.Order"/>.
		/// </summary>
		/// <param name="fail">Описание ошибки.</param>
		protected void RaiseOrderFailed(OrderFail fail)
		{
			if (fail == null)
				throw new ArgumentNullException("fail");

			this.AddOrderInfoLog(fail.Order, fail.Order.Id == 0 ? "OrderFailed" : "OrderCancelFailed", () => Environment.NewLine + fail.Error);

			var fails = new[] { fail };

			if (fail.Order.Type == OrderTypes.Conditional)
			{
				if (fail.Order.Id == 0)
					RaiseStopOrdersRegisterFailed(fails);
				else
					RaiseStopOrdersCancelFailed(fails);
			}
			else
			{
				if (fail.Order.Id == 0)
					RaiseOrdersRegisterFailed(fails);
				else
					RaiseOrdersCancelFailed(fails);
			}
		}

		/// <summary>
		/// Вызвать событие <see cref="NewMyTrades"/>.
		/// </summary>
		/// <param name="trades">Мои сделки, которые нужно передать в событие.</param>
		protected virtual void RaiseNewMyTrades(IEnumerable<MyTrade> trades)
		{
			_newMyTradesEvent.Push(trades);
		}

		/// <summary>
		/// Вызвать событие <see cref="NewTrades"/>.
		/// </summary>
		/// <param name="trade">Cделка, которую нужно передать в событие.</param>
		protected void RaiseNewTrade(Trade trade)
		{
			RaiseNewTrades(new[] { trade });
		}

		/// <summary>
		/// Вызвать событие <see cref="NewTrades"/>.
		/// </summary>
		/// <param name="trades">Cделки, которые нужно передать в событие.</param>
		protected virtual void RaiseNewTrades(IEnumerable<Trade> trades)
		{
			_newTradesEvent.Push(trades);
		}

		/// <summary>
		/// Вызвать событие <see cref="RaiseNewOrders"/> или <see cref="RaiseNewStopOrders"/> в зависимости от типа заявки.
		/// </summary>
		/// <param name="order">Заявка, которая была зарегистрирована в торговой системе.</param>
		protected void RaiseNewOrder(Order order)
		{
			if (order == null)
				throw new ArgumentNullException("order");

			this.AddOrderInfoLog(order, "New order");

			if (order.Type == OrderTypes.Conditional)
				RaiseNewStopOrders(new[] { order });
			else
				RaiseNewOrders(new[] { order });
		}

		/// <summary>
		/// Вызвать событие <see cref="RaiseOrdersChanged"/> или <see cref="RaiseStopOrdersChanged"/> в зависимости от типа заявки.
		/// </summary>
		/// <param name="order">Заявка, которая была изменена торговой системой.</param>
		protected void RaiseOrderChanged(Order order)
		{
			if (order == null)
				throw new ArgumentNullException("order");

			this.AddOrderInfoLog(order, "Order changed");

			if (order.Type == OrderTypes.Conditional)
				RaiseStopOrdersChanged(new[] { order });
			else
				RaiseOrdersChanged(new[] { order });
		}

		/// <summary>
		/// Вызвать событие <see cref="NewOrders"/>.
		/// </summary>
		/// <param name="orders">Заявки, которые нужно передать в событие.</param>
		protected virtual void RaiseNewOrders(IEnumerable<Order> orders)
		{
			_newOrdersEvent.Push(orders);
		}

		/// <summary>
		/// Вызвать событие <see cref="NewStopOrders"/>.
		/// </summary>
		/// <param name="stopOrders">Стоп-заявки, которые нужно передать в событие.</param>
		protected virtual void RaiseNewStopOrders(IEnumerable<Order> stopOrders)
		{
			_newStopOrdersEvent.Push(stopOrders);
		}

		/// <summary>
		/// Вызвать событие <see cref="OrdersChanged"/>.
		/// </summary>
		/// <param name="orders">Заявки, которые нужно передать в событие.</param>
		protected virtual void RaiseOrdersChanged(IEnumerable<Order> orders)
		{
			orders.FillLatencyCancellation();
			_ordersChangedEvent.Push(orders);
		}

		/// <summary>
		/// Вызвать событие <see cref="StopOrdersChanged"/>.
		/// </summary>
		/// <param name="stopOrders">Стоп-заявки, которые нужно передать в событие.</param>
		protected virtual void RaiseStopOrdersChanged(IEnumerable<Order> stopOrders)
		{
			stopOrders.FillLatencyCancellation();
			_stopOrdersChangedEvent.Push(stopOrders);
		}

		/// <summary>
		/// Вызвать событие <see cref="OrdersRegisterFailed"/>.
		/// </summary>
		/// <param name="fails">Информация об ошибках, которую нужно передать в событие.</param>
		protected virtual void RaiseOrdersRegisterFailed(IEnumerable<OrderFail> fails)
		{
			fails.DeleteLatencyMonitoring(true);
			_ordersRegisterFailedEvent.Push(fails);
		}

		/// <summary>
		/// Вызвать событие <see cref="OrdersCancelFailed"/>.
		/// </summary>
		/// <param name="fails">Информация об ошибках, которую нужно передать в событие.</param>
		protected virtual void RaiseOrdersCancelFailed(IEnumerable<OrderFail> fails)
		{
			fails.DeleteLatencyMonitoring(false);
			_ordersCancelFailedEvent.Push(fails);
		}

		/// <summary>
		/// Вызвать событие <see cref="StopOrdersRegisterFailed"/>.
		/// </summary>
		/// <param name="fails">Информация об ошибках, которую нужно передать в событие.</param>
		protected virtual void RaiseStopOrdersRegisterFailed(IEnumerable<OrderFail> fails)
		{
			fails.DeleteLatencyMonitoring(true);
			_stopOrdersRegisterFailedEvent.Push(fails);
		}

		/// <summary>
		/// Вызвать событие <see cref="StopOrdersCancelFailed"/>.
		/// </summary>
		/// <param name="fails">Информация об ошибках, которую нужно передать в событие.</param>
		protected virtual void RaiseStopOrdersCancelFailed(IEnumerable<OrderFail> fails)
		{
			fails.DeleteLatencyMonitoring(false);
			_stopOrdersCancelFailedEvent.Push(fails);
		}

		/// <summary>
		/// Вызвать событие <see cref="NewSecurities"/>.
		/// </summary>
		/// <param name="securities">Инструменты, которые нужно передать в событие.</param>
		protected virtual void RaiseNewSecurities(IEnumerable<Security> securities)
		{
			_newSecuritiesEvent.Push(securities);
		}

		/// <summary>
		/// Вызвать событие <see cref="SecuritiesChanged"/>.
		/// </summary>
		/// <param name="securities">Инструменты, которые нужно передать в событие.</param>
		protected virtual void RaiseSecuritiesChanged(IEnumerable<Security> securities)
		{
			_securitiesChangedEvent.Push(securities);
		}

		/// <summary>
		/// Вызвать событие <see cref="SecuritiesChanged"/>.
		/// </summary>
		/// <param name="security">Инструмент, который нужно передать в событие.</param>
		protected void RaiseSecurityChanged(Security security)
		{
			RaiseSecuritiesChanged(new[] { security });
		}

		/// <summary>
		/// Вызвать событие <see cref="NewPortfolios"/>.
		/// </summary>
		/// <param name="portfolios">Портфели, которые нужно передать в событие.</param>
		protected virtual void RaiseNewPortfolios(IEnumerable<Portfolio> portfolios)
		{
			_newPortfoliosEvent.Push(portfolios);
		}

		/// <summary>
		/// Вызвать событие <see cref="PortfoliosChanged"/>.
		/// </summary>
		/// <param name="portfolios">Портфели, которые нужно передать в событие.</param>
		protected virtual void RaisePortfoliosChanged(IEnumerable<Portfolio> portfolios)
		{
			_portfoliosChangedEvent.Push(portfolios);
		}

		/// <summary>
		/// Вызвать событие <see cref="NewPositions"/>.
		/// </summary>
		/// <param name="positions">Позиции, которые нужно передать в событие.</param>
		protected virtual void RaiseNewPositions(IEnumerable<Position> positions)
		{
			_newPositionsEvent.Push(positions);
		}

		/// <summary>
		/// Вызвать событие <see cref="PositionsChanged"/>.
		/// </summary>
		/// <param name="positions">Позиции, которые нужно передать в событие.</param>
		protected virtual void RaisePositionsChanged(IEnumerable<Position> positions)
		{
			_positionsChangedEvent.Push(positions);
		}

		/// <summary>
		/// Вызвать событие <see cref="NewMarketDepths"/>.
		/// </summary>
		/// <param name="marketDepths">Стаканы, которые нужно передать в событие.</param>
		protected virtual void RaiseNewMarketDepths(IEnumerable<MarketDepth> marketDepths)
		{
			_newMarketDepthsEvent.Push(marketDepths);
		}

		/// <summary>
		/// Вызвать событие <see cref="MarketDepthsChanged"/>.
		/// </summary>
		/// <param name="marketDepths">Стаканы, которые нужно передать в событие.</param>
		protected virtual void RaiseMarketDepthsChanged(IEnumerable<MarketDepth> marketDepths)
		{
			_marketDepthsChangedEvent.Push(marketDepths);
		}


		/// <summary>
		/// Вызвать событие <see cref="MarketDepthsChanged"/>.
		/// </summary>
		/// <param name="marketDepth">Стакан, который нужно передать в событие.</param>
		protected virtual void RaiseMarketDepthChanged(MarketDepth marketDepth)
		{
			if (marketDepth == null)
				throw new ArgumentNullException("marketDepth");

			var security = marketDepth.Security;

			RaiseMarketDepthsChanged(new[] { marketDepth });

			if (UpdateSecurityOnEachEvent)
			{
				using (security.BeginUpdate())
				{
					security.BestBid = marketDepth.BestBid.CloneNullable();
					security.BestAsk = marketDepth.BestAsk.CloneNullable();
					security.LastChangeTime = marketDepth.LastChangeTime;
				}
				RaiseSecurityChanged(security);
			}
		}
		
		/// <summary>
		/// Вызвать событие <see cref="NewOrderLogItems"/>.
		/// </summary>
		/// <param name="item">Строчка лога заявок.</param>
		protected void RaiseNewOrderLogItem(OrderLogItem item)
		{
			RaiseNewOrderLogItems(new[] { item });
		}

		/// <summary>
		/// Вызвать событие <see cref="NewOrderLogItems"/>.
		/// </summary>
		/// <param name="items">Строчки лога заявок.</param>
		protected virtual void RaiseNewOrderLogItems(IEnumerable<OrderLogItem> items)
		{
			NewOrderLogItems.SafeInvoke(items);
		}

		/// <summary>
		/// Вызвать событие <see cref="Connected"/>.
		/// </summary>
		protected virtual void RaiseConnected()
		{
			Connected.SafeInvoke();
		}

		/// <summary>
		/// Вызвать событие <see cref="Disconnected"/>.
		/// </summary>
		protected virtual void RaiseDisconnected()
		{
			Disconnected.SafeInvoke();
		}

		/// <summary>
		/// Вызвать событие <see cref="ConnectionError"/>.
		/// </summary>
		/// <param name="exception">Ошибка соединения.</param>
		protected virtual void RaiseConnectionError(Exception exception)
		{
			if (exception == null)
				throw new ArgumentNullException("exception");

			ConnectionError.SafeInvoke(exception);
			this.AddErrorLog(exception);
		}

		/// <summary>
		/// Число ошибок, переданное через событие <see cref="ProcessDataError"/>.
		/// </summary>
		public int DataErrorCount { get; private set; }

		/// <summary>
		/// Вызвать событие <see cref="ProcessDataError"/>.
		/// </summary>
		/// <param name="exception">Ошибка обработки данных.</param>
		protected virtual void RaiseProcessDataError(Exception exception)
		{
			if (exception == null)
				throw new ArgumentNullException("exception");

			DataErrorCount++;

			this.AddErrorLog(exception);
			ProcessDataError.SafeInvoke(exception);
		}

		/// <summary>
		/// Вызвать событие <see cref="NewDataExported"/>.
		/// </summary>
		protected virtual void RaiseNewDataExported()
		{
			NewDataExported.SafeInvoke();
		}

		/// <summary>
		/// Вызвать событие <see cref="StockSharp.Algo.ReConnectionSettings.ConnectionRestored"/>.
		/// </summary>
		protected virtual void RaiseConnectionRestored()
		{
			ReConnectionSettings.RaiseConnectionRestored();
		}

		/// <summary>
		/// Вызвать событие <see cref="StockSharp.Algo.ReConnectionSettings.ConnectionAttemptError"/>.
		/// </summary>
		/// <param name="exception">Ошибка соединения.</param>
		protected virtual void RaiseConnectionAttemptError(Exception exception)
		{
			ReConnectionSettings.RaiseConnectionAttemptError(exception);
			this.AddErrorLog(exception);
		}

		/// <summary>
		/// Вызвать событие <see cref="StockSharp.Algo.ReConnectionSettings.ExportTimeOut"/>.
		/// </summary>
		protected virtual void RaiseExportTimeOut()
		{
			ReConnectionSettings.RaiseExportTimeOut();
		}

		/// <summary>
		/// Вызвать событие <see cref="MarketTimeChanged"/>.
		/// </summary>
		/// <param name="diff">Разница во времени, прошедшее с последнего вызова события. Первый раз событие передает значение <see cref="TimeSpan.Zero"/>.</param>
		protected virtual void RaiseMarketTimeChanged(TimeSpan diff)
		{
			MarketTimeChanged.SafeInvoke(diff);
		}

		/// <summary>
		/// Получить инструмент по коду. Если инструмент не найден, то для создания инструмента вызывается <see cref="IEntityFactory.CreateSecurity"/>.
		/// </summary>
		/// <param name="id">Идентификатор инструмента.</param>
		/// <param name="changeSecurity">Обработчик, изменяющий инструмент. Возвращает true, если инструмент был изменен, и необходимо вызвать <see cref="ITrader.SecuritiesChanged"/>.</param>
		/// <returns>Инструмент.</returns>
		protected Security GetSecurity(string id, Func<Security, bool> changeSecurity)
		{
			return GetSecurity(id, EntityFactory.CreateSecurity, changeSecurity);
		}

		/// <summary>
		/// Получить инструмент по коду. Если инструмент не найден, то для создания инструмента вызывается <paramref name="createSecurity"/>.
		/// </summary>
		/// <param name="id">Идентификатор инструмента.</param>
		/// <param name="createSecurity">Обработчик, создающий инструмент.</param>
		/// <param name="changeSecurity">Обработчик, изменяющий инструмент. Возвращает true, если инструмент был изменен, и необходимо вызвать <see cref="ITrader.SecuritiesChanged"/>.</param>
		/// <returns>Инструмент.</returns>
		protected Security GetSecurity(string id, Func<string, Security> createSecurity, Func<Security, bool> changeSecurity)
		{
			if (id.IsEmpty())
				throw new ArgumentNullException("id");

			if (createSecurity == null)
				throw new ArgumentNullException("createSecurity");

			if (changeSecurity == null)
				throw new ArgumentNullException("changeSecurity");

			bool isNew;

			var security = _securities.SafeAdd(id, key =>
			{
				var s = createSecurity(key);

				if (s == null)
					throw new InvalidOperationException("Инструмент для идентификатора '{0}' не был создан.".Put(key));

				if (s.ExtensionInfo == null)
					s.ExtensionInfo = new Dictionary<object, object>();

				s.Trader = this;
				return s;
			}, out isNew);

			var isChanged = changeSecurity(security);

			if (isNew)
			{
				if (security.ExchangeBoard == null)
					throw new InvalidOperationException("Инструмент '{0}' не имеет информацию о бирже.".Put(id));

				_exchangeBoards.TryAdd(security.ExchangeBoard);
				RaiseNewSecurities(new[] { security });
			}
			else if (isChanged)
				RaiseSecurityChanged(security);

			lock (_suspendSync)
			{
				var evts = _suspendedSecurityEvents.TryGetValue(id);

				if (evts != null)
				{
					_suspendedSecurityEvents.Remove(id);

					foreach (var evt in evts)
						evt(security);
				}
			}

			return security;
		}

		/// <summary>
		/// Добавить ассоциацию между инструментов и его идентификатором в торговой системе.
		/// </summary>
		/// <param name="security">Инструмент.</param>
		/// <param name="nativeSecurityId">Идентификатор инструмента, используемый в торговой системе (например, число или <see cref="Guid"/>).</param>
		protected void AddNativeSecurityId(Security security, object nativeSecurityId)
		{
			if (security == null)
				throw new ArgumentNullException("security");

			if (nativeSecurityId == null)
				throw new ArgumentNullException("nativeSecurityId");

			lock (_suspendSync)
			{
				var sec = _nativeIdSecurities.TryGetValue(nativeSecurityId);

				if (sec == null)
					_nativeIdSecurities.Add(nativeSecurityId, security);
				else
				{
					if (sec != security)
						throw new InvalidOperationException("Инструменты {0} и {1} имеют одинаковый идентификатор торговой системы {2}.".Put(security.Id, sec.Id, nativeSecurityId));
				}

				var evts = _suspendedNativeSecurityEvents.TryGetValue(nativeSecurityId);

				if (evts != null)
				{
					_suspendedNativeSecurityEvents.Remove(nativeSecurityId);

					foreach (var evt in evts)
						evt(security);
				}
			}
		}

		/// <summary>
		/// Получить идентификатор инструмента в торговой системе.
		/// </summary>
		/// <param name="security">Инструмент.</param>
		/// <returns>Идентификатор инструмента, используемый в торговой системе (например, число или <see cref="Guid"/>).</returns>
		protected object GetNativeId(Security security)
		{
			if (security == null)
				throw new ArgumentNullException("security");

			lock (_suspendSync)
			{
				return _nativeIdSecurities.FirstOrDefault(p => p.Value == security).Value;
			}
		}

		/// <summary>
		/// Обновить позицию по портфелю и инструменту.
		/// </summary>
		/// <param name="portfolio">Портфель, по которому необходимо получить позицию.</param>
		/// <param name="security">Инструмент, по которому необходимо получить позицию.</param>
		/// <param name="changePosition">Обработчик, изменяющий позицию. Возвращает true, если позиция была изменена, и необходимо вызвать <see cref="ITrader.PositionsChanged"/>.</param>
		protected void UpdatePosition(Portfolio portfolio, Security security, Func<Position, bool> changePosition)
		{
			UpdatePosition(portfolio, security, string.Empty, changePosition);
		}

		/// <summary>
		/// Обновить позицию по портфелю и инструменту.
		/// </summary>
		/// <param name="portfolio">Портфель, по которому необходимо получить позицию.</param>
		/// <param name="security">Инструмент, по которому необходимо получить позицию.</param>
		/// <param name="depoName">Название депозитария, где находится физически ценная бумага.
		/// По-умолчанию передается пустая строка, что означает суммарную позицию по всем депозитариям.</param>
		/// <param name="changePosition">Обработчик, изменяющий позицию. Возвращает true, если позиция была изменена, и необходимо вызвать <see cref="ITrader.PositionsChanged"/>.</param>
		protected void UpdatePosition(Portfolio portfolio, Security security, string depoName, Func<Position, bool> changePosition)
		{
			if (portfolio == null)
				throw new ArgumentNullException("portfolio");

			if (security == null)
				throw new ArgumentNullException("security");

			if (changePosition == null)
				throw new ArgumentNullException("changePosition");

			var position = GetPosition(portfolio, security, depoName);

			if (changePosition(position))
				RaisePositionsChanged(new[] { position });
		}

		/// <summary>
		/// Получить сделку по инструменту и номеру. Если сделка не зарегистрирована, то она создается через <paramref name="createTrade"/>.
		/// </summary>
		/// <param name="security">Инструмент.</param>
		/// <param name="id">Номер сделки.</param>
		/// <param name="createTrade">Обработчик, создающий сделку.</param>
		/// <returns>Сделка.</returns>
		protected Trade GetTrade(Security security, long id, Func<long, Trade> createTrade)
		{
			if (createTrade == null)
				throw new ArgumentNullException("createTrade");

			var isNew = false;

			var trade = _cache.GetTrade(security, id, key =>
			{
				isNew = true;

				var t = createTrade(key);

				if (t == null)
					throw new InvalidOperationException("Сделка под номером {0} не была создана.".Put(id));

				return t;
			});

			if (isNew)
				RaiseNewTrade(trade);

			return trade;
		}

		/// <summary>
		/// Получить заявку по инструменту и номеру. Если заявка не зарегистрирована, то она создается через <paramref name="createOrder"/>.
		/// </summary>
		/// <param name="security">Инструмент.</param>
		/// <param name="id">Номер заявки.</param>
		/// <param name="createOrder">Обработчик, создающий заявку.</param>
		/// <param name="changeOrder">Обработчик, изменяющий заявку. Возвращает true, если заявка была изменена, и необходимо вызвать <see cref="ITrader.OrdersChanged"/>.</param>
		/// <returns>Заявка.</returns>
		protected Order GetOrder(Security security, long id, Func<long, Order> createOrder, Func<Order, bool> changeOrder)
		{
			if (createOrder == null)
				throw new ArgumentNullException("createOrder");

			if (changeOrder == null)
				throw new ArgumentNullException("changeOrder");

			var isNew = false;

			var order = _cache.GetOrder(security, id, key =>
			{
				isNew = true;

				var o = createOrder(key);

				if (o == null)
					throw new InvalidOperationException("Заявка под номером {0} не была создана.".Put(id));

				o.LatencyRegistration = o.GetLatency(true);

				o.Trader = this;

				if (o.ExtensionInfo == null)
					o.ExtensionInfo = new Dictionary<object, object>();

				return o;
			});

			var isChanged = changeOrder(order);

			if (isNew)
			{
				if (order.TransactionId != 0)
				{
					AddOrderByTransactionId(order);
				}

				RaiseNewOrder(order);
			}
			else if (isChanged)
				RaiseOrderChanged(order);

			ProcessMyTrades(order, order.Id, _notOrderedByIdMyTrades);
			ProcessMyTrades(order, order.TransactionId, _notOrderedByTransactionMyTrades);

			var changedStopOrders = new List<Order>();

			_orderStopOrderAssociations.SyncDo(d =>
			{
				var collection = d.TryGetValue(id);

				if (collection != null)
				{
					foreach (var pair in collection)
					{
						pair.Second(pair.First, order);
						changedStopOrders.TryAdd(pair.First);
					}

					d.Remove(id);
				}
			});

			RaiseStopOrdersChanged(changedStopOrders);

			return order;
		}

		private void AddOrderByTransactionId(Order order)
		{
			if (order == null)
				throw new ArgumentNullException("order");

			((order.Type == OrderTypes.Conditional) ? _stopOrdersByTransactionId : _ordersByTransactionId).SyncDo(d =>
			{
				var o = d.TryGetValue(order.TransactionId);

				if (o == null)
					d.Add(order.TransactionId, order);
				else if (o != order)
					throw new InvalidOperationException("Заявки {0} и {1} имеют одинаковый номером транзакции {2}.".Put(o.Id, order.Id, order.TransactionId));
			});
		}

		private void ProcessMyTrades(Order order, long id, SynchronizedDictionary<long, List<Trade>> notOrderedTrades)
		{
			var trades = notOrderedTrades.SyncGet(d =>
			{
				var value = d.TryGetValue(id);

				if (value != null)
				{
					d.Remove(id);
					return (IEnumerable<Trade>)value;
				}
				else
					return new Trade[0];
			});

			var now = LoggingHelper.Now;
			trades.ForEach(trade => AddMyTrade(order, trade, now, t => { }));
		}

		/// <summary>
		/// Добавить собственную сделку на основе информации об инструменте, тиковой сделке и номере заявки. Если заявка с указанным номером
		/// <paramref name="orderId"/> отсутствует, то информация о сделке сохраняется до тех пор, пока не будет получена необходимая заявка.
		/// </summary>
		/// <param name="security">Инструмент.</param>
		/// <param name="orderId">Номер заявки. Если передается нулевое значение, то заявка будет искаться по <paramref name="orderTransactionId"/>.</param>
		/// <param name="orderTransactionId">Номер транзакции заявки. Если передается нулевое значение, то заявка будет искаться по <paramref name="orderId"/>.</param>
		/// <param name="trade">Тиковая сделка.</param>
		protected void AddMyTrade(Security security, long orderId, long orderTransactionId, Trade trade)
		{
			if (trade == null)
				throw new ArgumentNullException("trade");

			AddMyTrade(security, orderId, orderTransactionId, trade.Id, id => trade, t => { });
		}

		/// <summary>
		/// Добавить собственную сделку на основе информации об инструменте, тиковой сделке и номере заявки. Если заявка с указанным номером
		/// <paramref name="orderId"/> отсутствует, то информация о сделке сохраняется до тех пор, пока не будет получена необходимая заявка.
		/// </summary>
		/// <param name="security">Инструмент.</param>
		/// <param name="orderId">Номер заявки. Если передается нулевое значение, то заявка будет искаться по <paramref name="orderTransactionId"/>.</param>
		/// <param name="orderTransactionId">Номер транзакции заявки. Если передается нулевое значение, то заявка будет искаться по <paramref name="orderId"/>.</param>
		/// <param name="tradeId">Номер сделки.</param>
		/// <param name="createTrade">Обработчик, создающий тиковую сделку, если она отсутствует в момент получения моей сделки.</param>
		/// <param name="initMyTrade">Обработчик, изменяющий мою сделку.</param>
		protected void AddMyTrade(Security security, long orderId, long orderTransactionId, long tradeId, Func<long, Trade> createTrade, Action<MyTrade> initMyTrade)
		{
			if (createTrade == null)
				throw new ArgumentNullException("createTrade");

			if (initMyTrade == null)
				throw new ArgumentNullException("initMyTrade");

			if (orderId == 0 && orderTransactionId == 0)
				throw new ArgumentOutOfRangeException("orderTransactionId", orderTransactionId, "Номер транзакции должен быть отличен от нуля.");

			var myTrade = _cache.TryGetMyTrade(security, tradeId);

			if (myTrade != null)
				return;

			var order = orderId != 0 ? _cache.TryGetOrder(security, orderId) : GetOrderByTransactionId(orderTransactionId);

			var trade = createTrade(tradeId);
			var now = LoggingHelper.Now;

			if (order == null)
			{
				(orderId != 0 ? _notOrderedByIdMyTrades : _notOrderedByTransactionMyTrades).SyncDo(d =>
				{
					order = orderId != 0 ? _cache.TryGetOrder(security, orderId) : GetOrderByTransactionId(orderTransactionId);

					if (order == null)
						d.SafeAdd(orderId != 0 ? orderId : orderTransactionId).Add(trade);
					else
						AddMyTrade(order, trade, now, initMyTrade);
				});
			}
			else
				AddMyTrade(order, trade, now, initMyTrade);
		}

		private void AddMyTrade(Order order, Trade trade, DateTime was, Action<MyTrade> initMyTrade)
		{
			if (order == null)
				throw new ArgumentNullException("order");

			if (trade == null)
				throw new ArgumentNullException("trade");

			if (initMyTrade == null)
				throw new ArgumentNullException("initMyTrade");

			var trades = _myTradesByOrder.SafeAdd(order);

			var isNew = false;

			var myTrade = _cache.GetMyTrade(order.Security, trade.Id, key =>
			{
				isNew = true;

				var t = EntityFactory.CreateMyTrade(order, trade);

				if (t.ExtensionInfo == null)
					t.ExtensionInfo = new Dictionary<object, object>();

				trades.Add(t);

				initMyTrade(t);

				t.Latency = TraderHelper.GetLatency(was, trade.Time, trade.Security.ExchangeBoard.Exchange);

				return t;
			});

			if (isNew)
				RaiseNewMyTrades(new[] { myTrade });

			// mika
			// http://stocksharp.com/forum/yaf_postst1072_Probliemy-so-sdielkami--pozitsiiami.aspx
			// из-за того, что сделки по заявке иногда приходит быстрее события NewOrders, неправильно расчитывается поза по стратегиям

			//var raiseOrderChanged = false;

			//trades.SyncDo(d =>
			//{
			//    var newBalance = order.Volume - d.Sum(t => t.Trade.Volume);

			//    if (order.Balance > newBalance)
			//    {
			//        raiseOrderChanged = true;

			//        order.Balance = newBalance;

			//        if (order.Balance == 0)
			//            order.State = OrderStates.Done;
			//    }
			//});

			//if (raiseOrderChanged)
			//    RaiseOrderChanged(order);
		}

		/// <summary>
		/// Получить портфель по названию. Если портфель не найден, то создается объект <see cref="Portfolio"/>,
		/// и инициализируется свойство <see cref="Portfolio.Name"/> значением, переданным в name.
		/// </summary>
		/// <param name="name">Название портфеля.</param>
		/// <returns>Портфель.</returns>
		protected Portfolio GetPortfolio(string name)
		{
			return GetPortfolio(name, null);
		}

		/// <summary>
		/// Получить портфель по названию. Если портфель не зарегистрирован, то он создается через <see cref="IEntityFactory.CreatePortfolio"/>.
		/// </summary>
		/// <param name="name">Название портфеля.</param>
		/// <param name="changePortfolio">Обработчик, изменяющий портфель.</param>
		/// <returns>Портфель.</returns>
		protected Portfolio GetPortfolio(string name, Func<Portfolio, bool> changePortfolio)
		{
			return GetPortfolio(name, EntityFactory.CreatePortfolio, changePortfolio);
		}

		/// <summary>
		/// Получить портфель по названию. Если портфель не зарегистрирован, то он создается через <paramref name="createPortfolio"/>.
		/// </summary>
		/// <param name="name">Название портфеля.</param>
		/// <param name="createPortfolio">Обработчик, создающий портфель.</param>
		/// <param name="changePortfolio">Обработчик, изменяющий портфель. Возвращает true, если портфель был изменен, и необходимо вызвать <see cref="ITrader.PortfoliosChanged"/>.</param>
		/// <returns>Портфель.</returns>
		protected Portfolio GetPortfolio(string name, Func<string, Portfolio> createPortfolio, Func<Portfolio, bool> changePortfolio)
		{
			if (name.IsEmpty())
				throw new ArgumentNullException("name");

			if (createPortfolio == null)
				throw new ArgumentNullException("createPortfolio");

			bool isNew;

			var portfolio = _portfolios.SafeAdd(name, key =>
			{
				var p = createPortfolio(key);

				if (p == null)
					throw new InvalidOperationException("Портфель для счета '{0}' не был создан.".Put(name));

				if (p.ExtensionInfo == null)
					p.ExtensionInfo = new Dictionary<object, object>();

				p.Trader = this;
				return p;
			}, out isNew);

			var isChanged = false;
			if (changePortfolio != null)
				isChanged = changePortfolio(portfolio);

			if (isNew)
				RaiseNewPortfolios(new[] { portfolio });
			else if (changePortfolio != null && isChanged)
				RaisePortfoliosChanged(new[] { portfolio });

			return portfolio;
		}

		/// <summary>
		/// Получить заявку по номеру транзакции <see cref="Order.TransactionId"/>.
		/// </summary>
		/// <param name="transactionId">Номер транзакции.</param>
		/// <returns>Заявка.</returns>
		protected Order GetOrderByTransactionId(long transactionId)
		{
			return _ordersByTransactionId.TryGetValue(transactionId);
		}

		/// <summary>
		/// Получить стоп-заявку по номеру транзакции <see cref="Order.TransactionId"/>.
		/// </summary>
		/// <param name="transactionId">Номер транзакции.</param>
		/// <returns>Стоп-заявка.</returns>
		protected Order GetStopOrderByTransactionId(long transactionId)
		{
			return _stopOrdersByTransactionId.TryGetValue(transactionId);
		}

		/// <summary>
		/// Добавить ассоциацию между номером обычной заявки и стоп-заявкой. Необходимо использовать, когда заявки и стоп-заявки приходят
		/// в разных потоках, и стоп-заявка с информацией о производной заявке пришла раньше, чем сама заявка.
		/// </summary>
		/// <param name="security">Инструмент.</param>
		/// <param name="derivedOrderId">Номер производной заявки.</param>
		/// <param name="stopOrder">Стоп-заявка.</param>
		/// <param name="handler">Обработчик, который будет вызван, как только придет информация о производной заявке.
		/// Первым параметром идет стоп-заявка, вторым - производная.</param>
		protected void AddDerivedOrder(Security security, long derivedOrderId, Order stopOrder, Action<Order, Order> handler)
		{
			if (stopOrder == null)
				throw new ArgumentNullException("stopOrder");

			if (handler == null)
				throw new ArgumentNullException("handler");

			if (stopOrder.Type != OrderTypes.Conditional)
				throw new ArgumentException("Заявка не является стоп-заявкой.", "stopOrder");

			_orderStopOrderAssociations.SyncDo(d =>
			{
				var order = _cache.TryGetOrder(security, derivedOrderId);

				if (order == null)
					d.Add(derivedOrderId, new RefPair<Order, Action<Order, Order>>(stopOrder, handler));
				else
					handler(stopOrder, order);
			});
		}

		/// <summary>
		/// Сгенерировать <see cref="Security.Id"/> инструмента.
		/// </summary>
		/// <param name="secCode">Код инструмента.</param>
		/// <param name="secClass">Класс инструмента.</param>
		/// <returns><see cref="Security.Id"/> инструмента.</returns>
		protected string CreateSecurityId(string secCode, string secClass)
		{
			return SecurityIdGenerator.GenerateId(secCode, secClass, GetSecurityClassInfo(secClass).Second);
		}

		/// <summary>
		/// Получить описание инструмента по классу из <see cref="SecurityClassInfo"/>.
		/// </summary>
		/// <param name="secClass">Класс инструмента.</param>
		/// <returns>Описание инструмента. Если класс не найден в <see cref="SecurityClassInfo"/>, то будет возвращено значение <see cref="DefaultSecurityClassInfo"/>.</returns>
		protected RefPair<SecurityTypes, ExchangeBoard> GetSecurityClassInfo(string secClass)
		{
			return SecurityClassInfo.TryGetValue(secClass) ?? DefaultSecurityClassInfo;
		}

		/// <summary>
		/// Обработать действие, ассоциированное с инструментом.
		/// </summary>
		/// <remarks>Необходимо в том случае, если информация по инструментам получается позднее, чем требуется обработка действия с инструментом.
		/// Например, позиция по инструменту пришла раньше, чем сама информация об инструменте.</remarks>
		/// <param name="securityId">Идентификатор инструмента <see cref="Security.Id"/>. Можеть быть null, если ассоциация идет через <paramref name="nativeSecurityId"/>.</param>
		/// <param name="nativeSecurityId">Идентификатор инструмента, используемый в торговой системе (например, число или <see cref="Guid"/>). Можеть быть null, если ассоциация идет через <paramref name="securityId"/>.</param>
		/// <param name="action">Действие.</param>
		/// <param name="ignoreIfNotExist">Не обрабатывать действие, если инструмент не найден.
		/// True, если обработка не требуется в случае отсутствия инструмента, иначе, false, и действие будет вызвано при появлении инструмента.</param>
		protected void ProcessSecurityAction(string securityId, object nativeSecurityId, Action<Security> action, bool ignoreIfNotExist = false)
		{
			if (securityId.IsEmpty() && nativeSecurityId == null)
				throw new ArgumentNullException("nativeSecurityId", "Ассоциативная информация пустая.");

			if (action == null)
				throw new ArgumentNullException("action");

			Security security;

			lock (_suspendSync)
			{
				security = nativeSecurityId != null ? _nativeIdSecurities.TryGetValue(nativeSecurityId) : _securities.TryGetValue(securityId);

				if (security == null)
				{
					if (!ignoreIfNotExist)
					{
						if (nativeSecurityId != null)
							_suspendedNativeSecurityEvents.SafeAdd(nativeSecurityId).Add(action);
						else
							_suspendedSecurityEvents.SafeAdd(securityId).Add(action);
					}

					return;
				}
			}

			action(security);
		}

		/// <summary>
		/// Начать экспорт события об изменении времени.
		/// </summary>
		protected virtual void StartMarketTimeExport()
		{
			if (null != _marketTimeChangedTimer)
				return;

			DateTime? lastMarketTime = null;

			_marketTimeChangedTimer = ThreadingHelper
				.Timer(() =>
				{
					var diff = (lastMarketTime == null) ? TimeSpan.Zero : LoggingHelper.Now - lastMarketTime.Value;
					lastMarketTime = LoggingHelper.Now;
						
					RaiseMarketTimeChanged(diff);
				})
				.Interval(MarketTimeChangedInterval);
		}

		/// <summary>
		/// Завершить экспорт события об изменении времени.
		/// </summary>
		protected virtual void StopMarketTimeExport()
		{
			if (null == _marketTimeChangedTimer)
				return;

			_marketTimeChangedTimer.Dispose();
			_marketTimeChangedTimer = null;
		}

		/// <summary>
		/// Запустить экспорт данных из торговой системы в программу (получение портфелей, инструментов, заявок и т.д.).
		/// </summary>
		public virtual void StartExport()
		{
			IsExportStarted = true;
			StartMarketTimeExport();
			RaiseNewDataExported();
			this.AddInfoLog("Экспорт запущен.");
		}

		/// <summary>
		/// Остановить экспорт данных из торговой системы в программу, запущенный через <see cref="ITrader.StartExport"/>.
		/// </summary>
		public virtual void StopExport()
		{
			_subscriptionManager.Stop();

			StopMarketTimeExport();
			IsExportStarted = false;
			this.AddInfoLog("Экспорт остановлен.");
		}

		/// <summary>
		/// Перезапустить экспорт данных из торговой системы через методы <see cref="ITrader.StopExport"/> и <see cref="ITrader.StartExport"/>.
		/// </summary>
		public virtual void ReStartExport()
		{
			_subscriptionManager.ReStart();
		}

		/// <summary>
		/// Очистить позиции <see cref="Positions"/>.
		/// </summary>
		protected void ClearPositions()
		{
			_positions.Clear();
		}

		/// <summary>
		/// Освободить занятые ресурсы. В частности, отключиться от торговой системы через <see cref="Disconnect"/>.
		/// </summary>
		protected override void DisposeManaged()
		{
			SafeDisconnect();
			_reConnectionManager.Dispose();

			// PYH:TODO: Такая очистка может очистить EvCont чужого трейдера, в итоге события перестанут проходить через
			// схему Push..EndSuspend

			//EventsContainer.Clear();

			base.DisposeManaged();

			MemoryStatistics.Instance.Traders.Remove(this);
		}

		/// <summary>
		/// Загрузить настройки.
		/// </summary>
		/// <param name="storage">Хранилище настроек.</param>
		public virtual void Load(SettingsStorage storage)
		{
			if (storage == null)
				throw new ArgumentNullException("storage");

			TradesKeepTime = storage.GetValue("TradesKeepTime", TimeSpan.FromDays(2));
			MarketTimeChangedInterval = storage.GetValue("MarketTimeChangedInterval", TimeSpan.FromMilliseconds(10));
			UpdateSecurityOnEachEvent = storage.GetValue("UpdateSecurityOnEachEvent", true);
		}

		/// <summary>
		/// Сохранить настройки.
		/// </summary>
		/// <param name="storage">Хранилище настроек.</param>
		public virtual void Save(SettingsStorage storage)
		{
			if (storage == null)
				throw new ArgumentNullException("storage");

			storage.SetValue("TradesKeepTime", TradesKeepTime);
			storage.SetValue("MarketTimeChangedInterval", MarketTimeChangedInterval);
			storage.SetValue("UpdateSecurityOnEachEvent", UpdateSecurityOnEachEvent);
		}
	}
}
